#!/usr/bin/env python 
# coding:utf-8
# @Time :9/11/18 09:33

import sys
import requests
import random
import time
import numpy as np

sys.path.append('..')
sys.path.append('../..')
sys.path.append('../../..')

from common.constant import UserAgents
from config.request_service_conf import REQUEST_SERVICE_CONF
from config.proxy_conf import DYNAMIC_PROXY_CONF
from common.logger import AppLogger


class RequestService(object):

    def __init__(self, logger, settings=None):
        self.logger = logger
        self._user_agent_list = UserAgents.all_user_agents
        self._settings = settings or REQUEST_SERVICE_CONF
        self._dynamic_settings = DYNAMIC_PROXY_CONF

    def get(self, url, params=None, use_proxy=True, proxy_type="static", dynamic_proxy_rate=None, host=None, timeout=None, cookies=None, headers=None):
        retry_times = 0
        timeout = timeout or self._settings.get("URL_REQUEST_TIME_OUT", 10)
        url_max_retry_times = self._settings.get("URL_MAX_RETRY_TIMES", 20)
        while retry_times < url_max_retry_times:
            retry_times += 1
            try:
                response = self.__equip_requester(use_proxy=use_proxy,
                                                  proxy_type=proxy_type,
                                                  dynamic_proxy_rate=dynamic_proxy_rate,
                                                  host=host,
                                                  headers=headers,
                                                  cookies=cookies).get(url, params=params, timeout=timeout)
                return response
            except BaseException as e:
                self.logger.warn("get request error happened. error={}".format(e))
                time.sleep(self._settings.get("URL_TIME_SLEEP_WHEN_ERROR_HAPPENED", 3))
                continue
        self.logger.error("get request error happened, "
                          "request max({}) times. but can't get proxy.".format(url_max_retry_times))
        raise Exception

    def post(self, url, form_data=None, use_proxy=True, proxy_type="static", dynamic_proxy_rate=None, host=None, timeout=None, cookies=None, headers=None):
        retry_times = 0
        timeout = timeout or self._settings.get("URL_REQUEST_TIME_OUT", 10)
        url_max_retry_times = self._settings.get("URL_MAX_RETRY_TIMES", 20)
        while retry_times < url_max_retry_times:
            retry_times += 1
            try:
                response = self.__equip_requester(use_proxy=use_proxy,
                                                  proxy_type=proxy_type,
                                                  dynamic_proxy_rate=dynamic_proxy_rate,
                                                  host=host,
                                                  headers=headers,
                                                  cookies=cookies).post(url, data=form_data, timeout=timeout)
                return response
            except BaseException as e:
                self.logger.warn("post request error happened. error={}".format(e))
                time.sleep(self._settings.get("URL_TIME_SLEEP_WHEN_ERROR_HAPPENED", 3))
                continue
        self.logger.error("post request error happened, "
                          "request max({}) times. but can't get proxy.".format(url_max_retry_times))
        raise Exception

    def __equip_requester(self, use_proxy=True, proxy_type="static", dynamic_proxy_rate=None, host=None, cookies=None, headers=None):
        session = requests.Session()
        headers = self.__load_random_useragent() if not headers else headers
        session.headers.update(headers)
        if cookies:
            session.cookies.update(cookies)
        if use_proxy:
            proxy_url = self.__grab_proxy(proxy_type=proxy_type, dynamic_proxy_rate=dynamic_proxy_rate, host=host)
            # proxy_url = proxy_url.encode('utf-8') if isinstance(proxy_url, bytes) else proxy_url
            self.logger.info("此时代理为 {}".format(proxy_url))
            session.proxies.update({'http': proxy_url})
        return session

    def __grab_proxy(self, proxy_type="static", dynamic_proxy_rate=None, host=None):
        if proxy_type not in ["static", "dynamic", "mix"]:
            self.logger.error("__grab_proxy error happened. proxy type must be 'static', 'dynamic' or 'mix'")
            raise ValueError

        if proxy_type == "mix":
            proxy_type = self.__choice_proxy_accord_dynamic_rate(dynamic_proxy_rate)

        if proxy_type == "dynamic" and not host:
            self.logger.error("__grab_proxy error happened. dynamic proxy must need host")
            raise Exception

        if proxy_type == "dynamic":
            dynamic_proxy = self.get_dynamic_proxy(host=host)
            if dynamic_proxy is {}:
                self.logger.error("__grab_proxy failed to get dynamic proxy")
                raise Exception
            return dynamic_proxy["http"]

        proxy_url = self._settings.get(proxy_type.upper()+'_PROXY_URL')
        retry_times = 0
        proxy_max_retry_times = self._settings.get("PROXY_MAX_RETRY_TIMES", 20)
        while retry_times < proxy_max_retry_times:
            retry_times += 1
            try:
                response = requests.get(proxy_url, timeout=self._settings.get("PROXY_REQUEST_TIME_OUT", 10))
                return response.content
            except BaseException as e:
                self.logger.warn("__grab_proxy request error happened. error={}".format(e))
                time.sleep(self._settings.get("PROXY_TIME_SLEEP_WHEN_ERROR_HAPPENED", 3))
                continue

        self.logger.error("__grab_proxy error happened, request max({}) times. but can't get proxy."
                          .format(proxy_max_retry_times))
        raise Exception

    def __choice_proxy_accord_dynamic_rate(self, dynamic_proxy_rate=None):
        proxy_typies = ["static", "dynamic"]
        dynamic_proxy_rate = dynamic_proxy_rate or self._settings.get("DEFAULT_DYNAMIC_PROXY_RATE", 0.3)
        selected_proxy = np.random.choice(proxy_typies, size=(1, 1), p=[1-dynamic_proxy_rate, dynamic_proxy_rate])[0][0]
        return selected_proxy

    def get_dynamic_proxy(self, host):
        for _ in range(3):
            try:
                r = requests.get('http://{}:{}/proxy/{}'.format(
                    self._dynamic_settings['host'],
                    self._dynamic_settings['port'],
                    host),
                    timeout=self._settings.get("URL_REQUEST_TIME_OUT", 10))
                if r is None or r.status_code != 200 or 'failed' in r.text or 'False' in r.text:
                    time.sleep(1)
                    self.logger.warn("动态代理服务异常, 重试...")
                    continue

                self.logger.info('鲲鹏 ip = {ip}'.format(ip=r.text))
                proxies = {'http': 'http://{host}'.format(host=r.text)}
                return proxies
            except Exception as e:
                self.logger.error("动态代理访问异常:")
                self.logger.exception(e)
                time.sleep(1)
        return {}

    def __load_random_useragent(self):
        user_agent = {"User-Agent": random.choice(self._user_agent_list)}
        return user_agent

    def __grab_cookies(self):
        pass


if __name__ == '__main__':
    url = "https://www.baidu.com/"
    logger = AppLogger("requirement.log").get_logger()
    requester = RequestService(logger)
    response = requester.get(url)
